﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GpuProgramCompiler
{
	class Crc
	{
		private UInt32[] ulTable = new UInt32[256];
		private static Crc instance = null;

		private Crc()
		{
			//0x04C11DB7 is the official polynomial used by PKZip, WinZip and Ethernet.
			UInt32 ulPolynomial = 0x04C11DB7;

			// 256 values representing ASCII character codes.
			for (int iCodes = 0; iCodes <= 0xFF; iCodes++)
			{
				ulTable[iCodes] = Reflect((UInt32)iCodes, (char)8) << 24;

				for (int iPos = 0; iPos < 8; iPos++)
				{
					ulTable[iCodes] = (ulTable[iCodes] << 1)
						^ (0 != (ulTable[iCodes] & (1 << 31)) ? ulPolynomial : 0);
				}

				ulTable[iCodes] = Reflect((UInt32)ulTable[iCodes], (char)32);
			}
		}

		private UInt32 Reflect(UInt32 ulReflect, char cChar)
		{
			UInt32 ulValue = 0;

			// Swap bit 0 for bit 7, bit 1 For bit 6, etc....
			for(int iPos = 1; iPos < (cChar + 1); iPos++)
			{
				if(0 != (ulReflect & 1))
				{
					ulValue |= ((UInt32)1 << (cChar - iPos));
				}
				ulReflect >>= 1;
			}

			return ulValue;
		}

		private void	GeneratePartialCrc(ref UInt32 ulCRC, Byte[] data)
		{
			foreach (Byte b in data)
			{
				//If your compiler complains about the following line, try changing each
				//    occurrence of *ulCRC with "((UInt32)*ulCRC)" or "*(UInt32 *)ulCRC".
				ulCRC = (ulCRC >> 8) ^ ulTable[(ulCRC & 0xFF) ^ b];
			}
		}

		public UInt32 GenerateCrc(Byte[] data)
		{
			UInt32 ulCRC = 0xffffffff; //Initilaize the CRC.
			GeneratePartialCrc(ref ulCRC, data);
			return (ulCRC ^ 0xffffffff); //Finalize the CRC and return.
		}

		public UInt32 GenerateCrc(String data)
		{
			System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
			return GenerateCrc(encoding.GetBytes(data));
		}

		public static Crc GetInstance()
		{
			if (instance == null)
				instance = new Crc();
			return instance;
		}
	}
}
